package cn.xzc.job.type.cron;

import cn.xzc.job.TimerContext;
import cn.xzc.job.Trigger;

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.TimeZone;

/**
 * 包含cron表达式和触发下次执行时间
 *
 * @author xzc
 * 当前时间 2024-02-12 21:30:00
 */
public class CronTrigger implements Trigger {

    private final CronExpression expression;

    private final ZoneId zoneId;


    /**
     * Build a {@code CronTrigger} from the pattern provided in the default time zone.
     *
     * @param expression a space-separated list of time fields, following cron
     *                   expression conventions
     */
    public CronTrigger(String expression) {
        this(expression, ZoneId.systemDefault());
    }

    /**
     * Build a {@code CronTrigger} from the pattern provided in the given time zone.
     *
     * @param expression a space-separated list of time fields, following cron
     *                   expression conventions
     * @param timeZone   a time zone in which the trigger times will be generated
     */
    public CronTrigger(String expression, TimeZone timeZone) {
        this(expression, timeZone.toZoneId());
    }

    /**
     * Build a {@code CronTrigger} from the pattern provided in the given time zone.
     *
     * @param expression a space-separated list of time fields, following cron
     *                   expression conventions
     * @param zoneId     a time zone in which the trigger times will be generated
     */
    public CronTrigger(String expression, ZoneId zoneId) {

        this.expression = CronExpression.parse(expression);
        this.zoneId = zoneId;
    }


    /**
     * Return the cron pattern that this trigger has been built with.
     */
    public String getExpression() {
        return this.expression.toString();
    }


    /**
     * Determine the next execution time according to the given trigger context.
     * 根据上次任务执行时间的相关信息和cron表达式获取下次执行的时间
     * Next execution times are calculated based on the {@linkplain TimerContext#lastCompletionTime completion time} of the
     * previous execution; therefore, overlapping executions won't occur.
     */
    @Override
    public Date nextExecutionTime(TimerContext timerContext) {
        Date date = timerContext.lastCompletionTime();
        //任务不是第一次执行的情况下【任务已经存在一次完成任务的情况】
        if (date != null) {
            //上一次任务计划开始的时间
            Date scheduled = timerContext.lastScheduledExecutionTime();
            if (scheduled != null && date.before(scheduled)) {
                // Previous task apparently executed too early...
                // Let's simply use the last calculated execution time then,
                // in order to prevent accidental re-fires in the same second.
                date = scheduled;
            }
        } else {
            date = new Date(timerContext.getClock().millis());
        }
        //date是上一次执行任务最后完成的时间或者开始第一次执行任务时的当前时间
        //根据date和时区获取dateTime
        ZonedDateTime dateTime = ZonedDateTime.ofInstant(date.toInstant(), this.zoneId);
        //获取下一次执行的时间
        ZonedDateTime next = this.expression.next(dateTime);
        return (next != null ? Date.from(next.toInstant()) : null);
    }


    @Override
    public boolean equals( Object other) {
        return (this == other || (other instanceof CronTrigger &&
                this.expression.equals(((CronTrigger) other).expression)));
    }

    @Override
    public int hashCode() {
        return this.expression.hashCode();
    }

    @Override
    public String toString() {
        return this.expression.toString();
    }

}
